﻿//This is a full-fledged Urban Müller's Brainfuck interpreter.
//Notable features include the scrollable buffer visualization
//and ability to set manual breakpoints pausing the execution. 

using System;
using System.Collections.Generic;
using DimOS.Constants;
using DimOS.DimSH;
using DimOS.GC;
using DimOS.Internals;
using Array = System.Array;
using SH = DimOS.DimSH.Shell;
using TERM = DimOS.DimSH.Terminal;
using HAL = Cosmos.Hardware;
using String = DimOS.Plugs.String;
using UI = DimOS.DimSH.Interface;

namespace DimOS.Brainfuck
{
    public static class Interpreter
    {
        public static Error Analyze(string code)
        {
            code = String.Trim(code);

            if (code == "" || (code.IndexOf('.') == -1 && code.IndexOf(',') == -1 && code.IndexOf('>') == -1
                && code.IndexOf('<') == -1 && code.IndexOf('+') == -1 && code.IndexOf('-') == -1 &&
                code.IndexOf('[') == -1 && code.IndexOf(']') == -1)) return Error.NotFound;

            var braceLeft = 0; var braceRight = 0;
            for (int i = 0; i < code.Length; i++) if (code[i] == '[')
                braceLeft++; else if (code[i] == ']') braceRight++;
            if (braceLeft != braceRight) return Error.UnmatchedSpecifier;

            return Error.None;
        }

        public static void Process(string code)
        {
            Heap.Monitor(); SH.BufferPointer = 0; var input = "";
            Array.Clear(SH.Buffer, 0, SH.Buffer.Length); UI.DrawBuffer();
            var until = HAL.RTC.Second + 1; if (until > 59) until -= 60;
            List<CharColor> prompt = new List<CharColor> { new CharColor
            (Separators.PromptBraceLeft, Colors.PromptEnclosure, Colors
            .SectionOutputBackground) }; for (int j = 0; j < Separators
            .Brainfuck.Length; j++) prompt.Add(new CharColor(Separators
            .Brainfuck[j], Colors.Prompt, Colors.SectionOutputBackground));
            prompt.Add(new CharColor(Separators.PromptBraceRight, Colors
            .PromptEnclosure, Colors.SectionOutputBackground)); prompt.Add(
            new CharColor(Separators.PromptSpecifier, Colors.Specifier, Colors
            .SectionOutputBackground)); prompt.Add(new CharColor(Tokens.Space,
            Colors.SectionOutputForeground, Colors.SectionOutputBackground)); 
            SH.Prompt(0, UI.OutputColumns - 1, UI.Rows - 1, 0, false, false,
            prompt); TERM.HideCursor(); UI.PanelView = true; string inline;
            var index = code.IndexOf('!'); if (index != -1) { inline = code
            .Substring(index + 1); code = code.Remove(index); } else inline
            = ""; var startPos = UI.X; var inputPos = UI.X; var outputPos = 0;
            UI.Foreground = Colors.SectionInputForeground;

            for (int i = 0; i < code.Length; i++)
            {                
                HAL.Keyboard.KeyMapping map; if (HAL.Global.Keyboard.GetMapping(out map)
                && map != null) { if (map.Key == ConsoleKey.Escape) { UI.PanelView =
                false; return; } if (map.Key == ConsoleKey.LeftArrow && UI.ColumnOffset
                > 0) { UI.ColumnOffset--; UI.DrawBuffer(); } else if (map.Key == ConsoleKey
                .RightArrow && UI.ColumnOffset < SH.Buffer.Length) { UI.ColumnOffset++; UI
                .DrawBuffer(); } } if (until == HAL.RTC.Second || until == HAL.RTC.Second - 1 )
                { UI.DrawClock(false); until = HAL.RTC.Second + 1; if (until > 59) until -= 60; }

                try { switch (code[i])
                {
                    case '.': UI.X = outputPos; UI.Y = UI.Rows - 3;
                    if (UI.X == UI.OutputColumns || (char)SH.Buffer
                    [SH.BufferPointer] == Tokens.NewLine || SH.Buffer
                    [SH.BufferPointer] == 10) { TERM.WriteLine();
                    if (!Settings.AdvancedTerminal) UI.DrawBuffer(); }
                    else { UI.Foreground = Colors.SectionOutputForeground;
                    TERM.Write((char) SH.Buffer[SH.BufferPointer], false);
                    UI.Foreground = Colors.SectionInputForeground; }
                    outputPos = UI.X; break;

                    case ',':
                    if (inline.Length != 0) { SH.Buffer[SH.BufferPointer]
                    = inline[0]; inline = inline.Remove(0, 1); continue; }
                    UI.X = inputPos; UI.Y = UI.Rows - 1;
                    string value; Heap.Monitor();
                    do
                    {   
                        until = HAL.RTC.Second + 1;
                        if (until > 59) until -= 60; TERM.ShowCursor();
                        while (!HAL.Global.Keyboard.GetMapping(out map))
                        {
                            if (until != HAL.RTC.Second) continue;

                            TERM.HideCursor();
                            UI.DrawClock(false);
                            TERM.ShowCursor();

                            until = HAL.RTC.Second + 1;
                            if (until > 59) until -= 60;
                        }
                        if (map != null) { if (map.Key == ConsoleKey.LeftArrow &&
                        UI.ColumnOffset > 0) { UI.ColumnOffset--; TERM.HideCursor();
                        UI.DrawBuffer(); } else if (map.Key == ConsoleKey.RightArrow
                        && UI.ColumnOffset < SH.Buffer.Length) { UI.ColumnOffset++;
                        TERM.HideCursor(); UI.DrawBuffer(); } }
                    }
                    while (map == null || ((map.Value < 32 || map.Value > 126) && map.Key !=
                    ConsoleKey.Escape && map.Value != Tokens.NewLine && map.Value != 10 && map
                    .Value != '\t')); Heap.Reclaim(); if (map.Key == ConsoleKey.Escape) { UI
                    .PanelView = false; return; } TERM.HideCursor(); value = map.Value.ToString
                    (); if (UI.X == UI.OutputColumns - 2) { input = input.Remove(0, 1); UI.X =
                    startPos; SH.InputFitStatus = 1; TERM.Write(input, false); UI.DrawInputFitIndicator
                    (UI.OutputColumns - 1, UI.Y); } if (map.Value == Tokens.NewLine || map.Value == 10)
                    value = Symbols.Paragraph; else if (map.Value == '\t') value = Symbols.Tab;
                    TERM.Write(value, false); input += value; SH.Buffer[SH.BufferPointer] = map
                    .Value; inputPos = UI.X; UI.DrawBufferCell(SH.BufferPointer, true); break;

                    case '>': UI.DrawBufferCell(SH.BufferPointer);
                    SH.BufferPointer++; if (SH.BufferPointer >=
                    SH.Buffer.Length) SH.BufferPointer = 0; UI
                    .DrawBufferCell(SH.BufferPointer, true); break;

                    case '<': UI.DrawBufferCell(SH.BufferPointer);
                    SH.BufferPointer--; if (SH.BufferPointer < 0)
                    SH.BufferPointer = SH.Buffer.Length - 1; UI
                    .DrawBufferCell(SH.BufferPointer, true); break;

                    case '+': SH.Buffer[SH.BufferPointer]++; UI
                    .DrawBufferCell(SH.BufferPointer, true); break;

                    case '-': SH.Buffer[SH.BufferPointer]--; UI
                    .DrawBufferCell(SH.BufferPointer, true); break;

                    case '[': if (SH.Buffer[SH.BufferPointer] == 0) { int indexL = 1; while(indexL > 0)
                    { i++; if (code[i] == '[') indexL++; else if (code[i] == ']') indexL--; } } break;

                    case ']': int indexR = 1; while (indexR > 0) { i--; if (code[i] == '[') indexR--;
                    else if (code[i] == ']') indexR++; } i--; break;

                    case '#': UI.X = prompt.Count; UI.Y = UI.Rows - 1; Heap.Monitor();
                    TERM.Write(UI.OutputSpaceSplitter.Remove(prompt.Count - 1), false,
                    false, false, false); UI.X = prompt.Count; UI.Foreground = Colors
                    .Warning; TERM.Write(Messages.Pause, false, false, false, false);
                    UI.Foreground = Colors.SectionInputForeground; do { until = HAL.RTC
                    .Second + 1; if (until > 59) until -= 60; while (!HAL.Global.Keyboard
                    .GetMapping(out map)) { if (until != HAL.RTC.Second) continue; UI
                    .DrawClock(false); until = HAL.RTC.Second + 1; if (until > 59) until
                    -= 60; } if (map != null) { if (map.Key == ConsoleKey.LeftArrow && UI
                    .ColumnOffset > 0) { UI.ColumnOffset--; UI.DrawBuffer(); } else if
                    (map.Key == ConsoleKey.RightArrow && UI.ColumnOffset < SH.Buffer
                    .Length) { UI.ColumnOffset++; UI.DrawBuffer(); } } } while (map == null
                    || (map.Value != Tokens.NewLine && map.Key != ConsoleKey.Escape)); Heap
                    .Reclaim(); if (map.Key == ConsoleKey.Escape) { UI.PanelView = false;
                    return; } UI.X = prompt.Count; TERM.Write(input, false); TERM.Write(UI
                    .OutputSpaceSplitter.Remove(0, input.Length + prompt.Count + 1), false);
                    break;
                } } catch { }
            } UI.PanelView = false; Heap.Reclaim();
        }
    }
}
